import { CharStreams, CommonTokenStream } from "antlr4ts";
import * as fs from "fs";
import { tscnLexer } from "./tscnLexer";
import { ArrayContext, AttributeContext, DictionaryContext, FileContext, InstanceContext, PairContext, Property_Context, Section_headerContext, tscnParser, ValueContext } from "./tscnParser";
import { ParseTreeWalker } from 'antlr4ts/tree/ParseTreeWalker'
import { tscnListener } from './tscnListener';
import { ParseTreeListener } from "antlr4ts/tree/ParseTreeListener";
import { ExtAttrName, NodeBaseAttrName, NodeType, SceneAttrName, SignalAttrName, SubAttrName, Tscn, TscnAttr, TscnExtResource, TscnNode, TscnProp, TscnSignal, TscnSubResource } from "../../model/tscn";
import { Gen } from "../../model/gen";

class MyListener implements tscnListener {
    scene: Tscn
    currentNode: TscnNode | TscnSubResource | null = null;
    lastParent: TscnNode | null = null;
    nameToNoe: Map<string, TscnNode> = new Map()
    gdx: string;
    constructor(fileName: string) {
        this.scene = new Tscn();
        this.gdx = "";
    }
    exitFile(ctx: FileContext) {
        this.gdx = Gen.tscn2gdx(this.scene);
    }
    /**
     * Enter a parse tree produced by `tscnParser.section_header`.
     * @param ctx the parse tree
     */
    enterSection_header(ctx: Section_headerContext) {
        let sectionName = ctx.ID().text;
        let attrs = ctx.attribute();

        // gd_scene
        if (sectionName == NodeType.SCENE) {
            attrs.forEach(a => {
                if (a.ID().text == SceneAttrName.STEPS) {
                    this.scene.steps = parseInt(a.value().text)
                } else if (a.ID().text == SceneAttrName.FORMAT) {
                    this.scene.fromat = parseInt(a.value().text)
                }
            })
        }
        // ext_resource
        if (sectionName == NodeType.EXT) {
            let path = "";
            let type = "";
            let id = 0;

            attrs.forEach(a => {
                if (a.ID().text == ExtAttrName.PATH) {
                    path = a.value().text.replace(/"/g, '')
                } else if (a.ID().text == ExtAttrName.TYPE) {
                    type = a.value().text.replace(/"/g, '')
                } else if (a.ID().text == ExtAttrName.ID) {
                    id = parseInt(a.value().text)
                }
            })
            this.scene.pushExt(new TscnExtResource(
                path,
                type,
                id
            ))
        }
        // sub_resource
        if (sectionName == NodeType.SUB) {
            let type = "";
            let id = 0;
            let hasOtherAttr: AttributeContext[] = [];
            attrs.forEach(a => {
                if (a.ID().text == SubAttrName.TYPE) {
                    type = a.value().text.replace(/"/g, '')
                } else if (a.ID().text == SubAttrName.ID) {
                    id = parseInt(a.value().text)
                } else {
                    hasOtherAttr.push(a);
                }
            })
            this.currentNode = new TscnSubResource(
                type,
                id
            )
            // 进行属性处理
            if (hasOtherAttr.length > 0) {
                hasOtherAttr.forEach(a => {
                    this.evalValue("attr", a)
                })
            }
            this.scene.pushSub(this.currentNode)
        }
        // connection
        if (sectionName == NodeType.SIGNAL) {
            let signal = "";
            let from = "";
            let to = "";
            let method = "";

            attrs.forEach(a => {
                if (a.ID().text == SignalAttrName.SIGNAL) {
                    signal = a.value().text.replace(/"/g, '')
                } else if (a.ID().text == SignalAttrName.FROM) {
                    from = a.value().text.replace(/"/g, '')
                } else if (a.ID().text == SignalAttrName.TO) {
                    to = a.value().text.replace(/"/g, '')
                } else if (a.ID().text == SignalAttrName.METHOD) {
                    method = a.value().text.replace(/"/g, '')
                }
            })
            this.scene.pushSignal(new TscnSignal(
                signal,
                from,
                to,
                method
            ))
        }

        // node
        if (sectionName == NodeType.NODE) {
            // 基础的Name和Type属性
            let name = "";
            let type = "";
            let parent = "-1";
            let hasOtherAttr: AttributeContext[] = [];

            attrs.forEach(a => {
                if (a.ID().text == NodeBaseAttrName.NAME) {
                    name = a.value().text.replace(/"/g, '')
                } else if (a.ID().text == NodeBaseAttrName.TYPE) {
                    type = a.value().text.replace(/"/g, '')
                } else if (a.ID().text == NodeBaseAttrName.PARENT) {
                    parent = a.value().text.replace(/"/g, '')
                } else {
                    hasOtherAttr.push(a);
                }
            })
            let tempNode = new TscnNode(
                name, type, parent
            )
            this.nameToNoe.set(name, tempNode);

            // console.log("node",parent,parent == "\".\"",parent == "")
            if (parent == ".") {
                // 匿名父节点
                this.lastParent?.children.push(tempNode)
            } else if (parent == "-1") {
                // 空父节点
                this.lastParent = tempNode;
                this.scene.pushRootNode(tempNode)
            } else if (parent) {
                //显示指定父节点
                let parentNode = this.nameToNoe.get(parent);
                if (parent == null) {
                    console.log("node", name, parent)
                    throw new Error("父节点查找失败")
                }
                parentNode?.children.push(tempNode)
            }
            this.currentNode = tempNode;

            // 进行属性处理
            if (hasOtherAttr.length > 0) {
                hasOtherAttr.forEach(a => {
                    this.evalValue("attr", a)
                })
            }
        }
    }
    /**
     * Enter a parse tree produced by `tscnParser.property_`.
     * @param ctx the parse tree
     */
    enterProperty_(ctx: Property_Context) {
        this.evalValue("prop", ctx);
    }
    // 计算值
    evalValue(
        type: "attr" | "prop",
        ctx: AttributeContext | Property_Context) {
        //值生成
        if (this.currentNode) {
            let name = ctx.ID().text;
            let value = this.enterValue(ctx.value());

            if (type == "attr") {
                // console.log("attr", this.currentNode, ctx.text);
                (this.currentNode as TscnNode).pushAttr(
                    new TscnAttr(name, value["type"], value["value"])
                )
            } else if (type == "prop") {
                this.currentNode.pushProp(
                    new TscnProp(name, value["type"], value["value"])
                )
            }
        }
    }



    /**
     * Enter a parse tree produced by `tscnParser.dictionary`.
     * @param ctx the parse tree
     */
    enterDictionary(ctx: DictionaryContext) {
        let valueArray: string[] = [];
        let pairCtx = ctx.pair();
        pairCtx.forEach(p => {
            valueArray.push(this.enterPair(p))
        })
        return `{ ${valueArray.join(",")} }`;
    }
    /**
     * Enter a parse tree produced by `tscnParser.pair`.
     * @param ctx the parse tree
     */
    enterPair(ctx: PairContext) {
        let key = ctx.STRING_().text.replace(/"/g, '');
        let value = this.enterValue(ctx.value())["value"]

        return `${key} : ${value}`
    }
    /**
     * Enter a parse tree produced by `tscnParser.array`.
     * @param ctx the parse tree
     */
    enterArray(ctx: ArrayContext) {
        let valueArray: string[] = [];
        let vCtx = ctx.value();
        vCtx.forEach(v => {
            valueArray.push(this.enterValue(v)["value"])
        })
        return `[${valueArray.join(",")}]`;
    }
    /**
     * Enter a parse tree produced by `tscnParser.value`.
     * @param ctx the parse tree
     */
    enterValue(ctx: ValueContext) {
        let result = {
            type: "",
            value: ""
        };
        if (ctx.INT()) {
            result = {
                type: "int",
                value: ctx.INT()?.toString() as string
            }
        } else if (ctx.FLOAT_()) {
            result = {
                type: "float",
                value: ctx.FLOAT_()?.toString() as string
            }
        } else if (ctx.STRING_()) {
            result = {
                type: "string",
                value: ctx.STRING_()?.toString().replace(/"/g, "*") as string
            }
        } else if (ctx.instance()) {
            result = {
                type: "instance",
                value: ctx.instance()?.text as string
            }
        } else if (ctx.array()) {
            result = {
                type: "array",
                value: this.enterArray(ctx.array() as ArrayContext) as string
            }
        } else if (ctx.dictionary()) {
            result = {
                type: "dictionary",
                value: this.enterDictionary(ctx.dictionary() as DictionaryContext) as string
            }
        } else {
            result = {
                type: "simple",
                value: ctx.text
            }
        }
        return result;
    }

}


function parseTscn(fileName: string) {
    let code = fs.readFileSync(fileName, "utf-8");
    let inputStream = CharStreams.fromString(code);
    let lexer = new tscnLexer(inputStream);
    let tokenStream = new CommonTokenStream(lexer);
    let parser = new tscnParser(tokenStream);
    let tree = parser.file();

    const listener = new MyListener(fileName);
    ParseTreeWalker.DEFAULT.walk(listener as ParseTreeListener, tree)

    fs.writeFileSync(
        fileName.replace(".tscn", ".json"),
        JSON.stringify(listener.scene, null, 2)
    )
    console.log("json ok", fileName)
    fs.writeFileSync(
        fileName.replace(".tscn", ".xml"),
        listener.gdx
    );
    console.log("gdx ok", fileName);
}

let fileNames = [
    "../../test/Main.tscn",
    "../../test/HUD.tscn",
    "../../test/Mob.tscn",
    "../../test/Player.tscn",
]
fileNames.forEach(name => {
    parseTscn(name)
})
export {
    parseTscn
}